package divestoclimb.checklist;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * This is an abstract class that handles backing up or restoring (actually, almost any
 * complex operation) on a set of items. Extend this class and implement the below
 * abstract methods to create a backup activity, a restore activity, or nearly anything
 * else that needs to operate on lots of things at once.
 * TODO: implement lifecycle. Interrupt worker thread on pause, then find a way to
 * resume where it left off.
 * @author Ben Roberts (divestoclimb@gmail.com)
 */
public abstract class AbsBackupRestore extends Activity implements Button.OnClickListener {
	
	private TextView mCurrentItem;
	private boolean mInitialized;
	private ProgressBar mProgressBar;
	private Thread mWorkThread;
	private boolean mFailure = false;

	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		// initialize UI
		setContentView(R.layout.progress_dialog);
		mCurrentItem = (TextView)findViewById(R.id.item);
		mProgressBar = (ProgressBar)findViewById(R.id.progress);
		
		Button cancel = (Button)findViewById(R.id.cancel);
		cancel.setOnClickListener(this);
	}

	@Override
	public void onResume() {
		super.onResume();
		if(! mInitialized) {
			// call getItems
			int count = getItemCount();
			if(count == 0) {
				setResult(D2cChecklist.RESULT_FAILED);
				finish();
			}
			mProgressBar.setMax(getItemCount());
			mProgressBar.setProgress(0);
			mInitialized = true;
		}
		// start worker thread on next item
		Thread t = new Thread(new ItemProcessor());
		t.start();
		mWorkThread = t;
	}

	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		// TODO: save items array, interrupt thread.
	}
	
	/**
	 * OnClickListener for the cancel button
	 */
	public void onClick(View v) {
		switch(v.getId()) {
		case R.id.cancel:
			mWorkThread.interrupt();
			break;
		}
	}
	
	protected class ItemProcessor implements Runnable {

		public void run() {
			String name;
			while((name = getNextItem()) != null) {
				// Dispatch message with item we're going to process
				messageHandler.sendMessage(Message.obtain(messageHandler, PROCESS_ITEM, name));
				// Do the work
				boolean success = processItem();
				if(success) {
					messageHandler.sendMessage(Message.obtain(messageHandler, ITEM_COMPLETE));
				} else {
					messageHandler.sendMessage(Message.obtain(messageHandler, ITEM_FAILED));
				}
				if(Thread.interrupted()) {
					messageHandler.sendMessage(Message.obtain(messageHandler, INTERRUPTED));
					break;
				}
			}
		}
	}
	
	// These are constants for messages' what properties
	private static final int PROCESS_ITEM = 1;
	private static final int ITEM_COMPLETE = 2;
	private static final int ITEM_FAILED = -2;
	private static final int INTERRUPTED = -1;
	
	private Handler messageHandler = new Handler() {
		
		@Override
		public void handleMessage(Message msg) {
			switch(msg.what) {
			case PROCESS_ITEM:
				// The thread told us what item is being processed. Update the
				// label.
				mCurrentItem.setText((String)msg.obj);
				break;
			case ITEM_FAILED:
				mFailure = true;
			case ITEM_COMPLETE:
				// The thread told us it finished an item. Increment the progress
				// bar.
				ProgressBar progress = mProgressBar;
				progress.incrementProgressBy(1);
				// If we reached the end, finish.
				if(progress.getProgress() == progress.getMax()) {
					setResult(mFailure? D2cChecklist.RESULT_FAILED: RESULT_OK);
					finish();
				}
				break;
			case INTERRUPTED:
				// The thread was interrupted, which only occurs when the user presses
				// the cancel button. Exit the activity.
				// TODO: an interrupt is fired onSaveInstanceState. Need to detect this. 
				setResult(RESULT_CANCELED);
				finish();
				break;
			}
		}
	};

	/**
	 * Returns the number of items that need to be backed up or restored.
	 * @return The number of items
	 */
	protected abstract int getItemCount();

	/**
	 * Returns a string representation of the next item, usually a name of some
	 * sort. This is used to update the progress information in the layout.
	 * @return A string that identifies this item
	 */
	protected abstract String getNextItem();

	/**
	 * Perform a backup or restore operation on the next item
	 * @param o The item to back up or restore
	 * @return true if successful, false otherwise
	 */
	protected abstract boolean processItem();
}